package hw_alg_oj

import org.scalatest.FunSuite


class HW2016AlgOJSuite extends FunSuite {
  test("1st debug") {
    /*
    [编程题]最高分是多少
时间限制：C/C++ 1秒，其他语言2秒

空间限制：C/C++ 64M，其他语言128M

老师想知道从某某同学当中，分数最高的是多少，现在请你编程模拟老师的询问。当然，老师有时候需要更新某位同学的成绩.

输入描述:
输入包括多组测试数据。
每组输入第一行是两个正整数N和M（0 < N <= 30000,0 < M < 5000）,分别代表学生的数目和操作的数目。
学生ID编号从1编到N。
第二行包含N个整数，代表这N个学生的初始成绩，其中第i个数代表ID为i的学生的成绩
接下来又M行，每一行有一个字符C（只取‘Q’或‘U’），和两个正整数A,B,当C为'Q'的时候, 表示这是一条询问操作，他询问ID从A到B（包括A,B）的学生当中，成绩最高的是多少
当C为‘U’的时候，表示这是一条更新操作，要求把ID为A的学生的成绩更改为B。

输出描述:
对于每一次询问操作，在一行里面输出最高成绩.

输入例子1:
5 7
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 4 5
U 2 9
Q 1 5

输出例子1:
5
6
5
9
     */

    import java.io.{BufferedReader, ByteArrayInputStream, InputStreamReader}

    val input =
      """5 7
        |1 2 3 4 5
        |Q 1 5
        |U 3 6
        |Q 3 4
        |Q 4 5
        |U 4 5
        |U 2 9
        |Q 1 5
        |5 7
        |1 2 3 4 5
        |Q 1 5
        |U 3 6
        |Q 3 4
        |Q 4 5
        |U 4 5
        |U 2 9
        |Q 1 5""".stripMargin
    val inputStream = new ByteArrayInputStream(input.getBytes())
    //    val inputStream = System.in
    val br = new BufferedReader(new InputStreamReader(inputStream))

    var line = br.readLine()
    var group = -1
    var initialScores: Array[Int] = null
    while (line != null) {
      if (group <= 0) { // initial
        val Array(_, optionNumString) = line.split(" ")
        group = optionNumString.toInt

        line = br.readLine()
        initialScores = line.split("\\s").map(_.toInt)

      } else { // option
        val Array(option, first, second) = line.split(" ")

        option match {
          case "Q" =>
            val (start, end) = if (first.toInt < second.toInt)
              (first.toInt - 1, second.toInt)
            else
              (second.toInt - 1, first.toInt)

            println(initialScores.slice(start, end).max)

          case "U" =>
            initialScores(first.toInt - 1) = second.toInt
        }

        // option count
        group -= 1
      }

      line = br.readLine()
    }
  }

  test("1st") {
    import java.io.{BufferedReader, InputStreamReader}

    object Main {
      def main(args: Array[String]): Unit = {
        val inputStream = System.in
        val br = new BufferedReader(new InputStreamReader(inputStream))

        var line = br.readLine()
        var optionCnt = -1
        var initialScores: Array[Int] = null
        while (line != null) {
          if (optionCnt <= 0) { // initial
            val Array(_, optionNumString) = line.split(" ")
            optionCnt = optionNumString.toInt

            line = br.readLine()
            initialScores = line.split("\\s").map(_.toInt)
          } else { // option
            val Array(option, first, second) = line.split(" ")

            option match {
              case "Q" =>
                val (start, end) = if (first.toInt < second.toInt)
                  (first.toInt - 1, second.toInt)
                else
                  (second.toInt - 1, first.toInt)

                println(initialScores.slice(start, end).max)

              case "U" =>
                initialScores(first.toInt - 1) = second.toInt
            }

            // option count
            optionCnt -= 1
          }

          line = br.readLine()
        }
      }
    }


  }

  test("2nd debug") {
    /*
    开发一个简单错误记录功能小模块，能够记录出错的代码所在的文件名称和行号。
处理:
1.记录最多8条错误记录，对相同的错误记录(即文件名称和行号完全匹配)只记录一条，错误计数增加；(文件所在的目录不同，文件名和行号相同也要合并)
2.超过16个字符的文件名称，只记录文件的最后有效16个字符；(如果文件名不同，而只是文件名的后16个字符和行号相同，也不要合并)
3.输入的文件可能带路径，记录文件名称不能带路径

输入描述:
一行或多行字符串。每行包括带路径文件名称，行号，以空格隔开。

    文件路径为windows格式

    如：E:\V1R2\product\fpgadrive.c 1325

输出描述:
将所有的记录统计并将结果输出，格式：文件名代码行数数目，一个空格隔开，如: fpgadrive.c 1325 1

    结果根据数目从多到少排序，数目相同的情况下，按照输入第一次出现顺序排序。

    如果超过8条记录，则只输出前8条记录.

    如果文件名的长度超过16个字符，则只输出后16个字符

输入例子1:
E:\V1R2\product\fpgadrive.c 1325

输出例子1:
fpgadrive.c 1325 1
     */

    import java.io.{BufferedReader, InputStreamReader, ByteArrayInputStream}

    object Main {
      def main(args: Array[String]): Unit = {
        val input =
          """E:\V1R2\product\fpgadrive.c 1325
            |E:\V1R2\product2\fpgadrive.c 1325
            |E:\V1R2\product\fpgadrive.c 1325
            |E:\V1R2\product\fpgadrive2.c 1325
            |C:\a.txt 1
            |D:\alg.scala 2
            |D:\alg.py 215
            |D:\alg.java 55
            |E:\V1R2\product\fpgadrive.c 1325
            |D:\1234567890123456.java 55
            |D:\0001234567890123456.java 55
            |D:\alg_alg_alg_alg_fpgadrive2.c 1325
            |E:\V1R2\product\fpgadrive.c 1325
            |""".stripMargin

        val inputStream = new ByteArrayInputStream(input.getBytes())
//        val inputStream = System.in

        val br = new BufferedReader(new InputStreamReader(inputStream))
        var line = br.readLine()

        // errorHistory 记录, 保存输入顺序: 文件名 + " " + 行号, 错误数
        val errorHistory = scala.collection.mutable.LinkedHashMap.empty[String, Int]

        while (line != null) {
          val fileNameWithCodeLine = line.split("\\\\").last
          if (errorHistory contains fileNameWithCodeLine)
            errorHistory(fileNameWithCodeLine) += 1
          else
            errorHistory += fileNameWithCodeLine -> 1

          line = br.readLine()
        }

        errorHistory.toArray.zipWithIndex.sortBy {
          case ((_, cnt), idx) =>
            (-cnt, idx)
        }.take(8).foreach {
          case ((fileNameWithCodeLine, cnt), _) =>
            val Array(fileName, codeLine) = fileNameWithCodeLine.split(" ")
            println(fileName.takeRight(16) + " " + codeLine + " " + cnt)
        }
      }
    }

    Main.main(null)

  }

  test("2nd") {
    import java.io.{BufferedReader, InputStreamReader, ByteArrayInputStream}

    object Main {
      def main(args: Array[String]): Unit = {
        val inputStream = System.in

        val br = new BufferedReader(new InputStreamReader(inputStream))
        var line = br.readLine()

        // errorHistory 记录, 保存输入顺序: 文件名 + " " + 行号, 错误数
        val errorHistory = scala.collection.mutable.LinkedHashMap.empty[String, Int]

        def format(line: String): String = {
          val Array(path, codeNum) = line.split(" ")
          val fileName = path.split("\\\\").last
          fileName.takeRight(16) + " " + codeNum
        }

        while (line != null) {
          val fileNameWithCodeLine = line.split("\\\\").last
          if (errorHistory contains fileNameWithCodeLine)
            errorHistory(fileNameWithCodeLine) += 1
          else
            errorHistory += fileNameWithCodeLine -> 1

          line = br.readLine()
        }

        errorHistory.toArray.zipWithIndex.sortBy {
          case ((_, cnt), idx) =>
            (-cnt, idx)
        }.take(8).foreach {
          case ((fileNameWithCodeLine, cnt), _) =>
            val Array(fileName, codeLine) = fileNameWithCodeLine.split(" ")
            println(fileName.takeRight(16) + " " + codeLine + " " + cnt)
        }
      }
    }

  }

  test("3rd debug") {
    /*
    [编程题]扑克牌大小
时间限制：C/C++ 10秒，其他语言20秒

空间限制：C/C++ 128M，其他语言256M

扑克牌游戏大家应该都比较熟悉了，一副牌由54张组成，含3~A，2各4张，小王1张，大王1张。牌面从小到大用如下字符和字符串表示（其中，小写joker表示小王，大写JOKER表示大王）:)
3 4 5 6 7 8 9 10 J Q K A 2 joker JOKER
输入两手牌，两手牌之间用“-”连接，每手牌的每张牌以空格分隔，“-”两边没有空格，如：4 4 4 4-joker JOKER
请比较两手牌大小，输出较大的牌，如果不存在比较关系则输出ERROR

基本规则：
（1）输入每手牌可能是个子，对子，顺子（连续5张），三个，炸弹（四个）和对王中的一种，不存在其他情况，由输入保证两手牌都是合法的，顺子已经从小到大排列；
（2）除了炸弹和对王可以和所有牌比较之外，其他类型的牌只能跟相同类型的存在比较关系（如，对子跟对子比较，三个跟三个比较），不考虑拆牌情况（如：将对子拆分成个子）
（3）大小规则跟大家平时了解的常见规则相同，个子，对子，三个比较牌面大小；顺子比较最小牌大小；炸弹大于前面所有的牌，炸弹之间比较牌面大小；对王是最大的牌；
（4）输入的两手牌不会出现相等的情况。

答案提示：
（1）除了炸弹和对王之外，其他必须同类型比较。
（2）输入已经保证合法性，不用检查输入是否是合法的牌。
（3）输入的顺子已经经过从小到大排序，因此不用再排序了.

输入描述:
输入两手牌，两手牌之间用“-”连接，每手牌的每张牌以空格分隔，“-”两边没有空格，如4 4 4 4-joker JOKER。

输出描述:
输出两手牌中较大的那手，不含连接符，扑克牌顺序不变，仍以空格隔开；如果不存在比较关系则输出ERROR。

输入例子1:
4 4 4 4-joker JOKER

输出例子1:
joker JOKER
     */

    object Main {

      import java.io.{BufferedReader, ByteArrayInputStream, InputStreamReader}

      def main(args: Array[String]): Unit = {
        val input =
          """4 4 4 4-joker JOKER
            |3 4 5 6 7-2 2
            |3 3 3-7 7 7
            |4 4 4 4-2 2
            |""".stripMargin
        val inputStream = new ByteArrayInputStream(input.getBytes())

        //        val inputStream = System.in

        val br = new BufferedReader(new InputStreamReader(inputStream))
        var line = br.readLine()

        val cardsMap = Map(
          "3" -> 0,
          "4" -> 1,
          "5" -> 2,
          "6" -> 3,
          "7" -> 4,
          "8" -> 5,
          "9" -> 6,
          "10" -> 7,
          "J" -> 8,
          "Q" -> 9,
          "K" -> 10,
          "A" -> 11,
          "2" -> 12,
          "joker" -> 13,
          "JOKER" -> 14
        )

        def score(cards: Array[String]): String = {
          cards.length match {
            case 1 | 3 | 5 => "normal" + cards.length
            case 2 =>
              if (cards.contains("JOKER") && cards.contains("joker"))
                "*"
              else
                "normal2"
            case 4 =>
              "*"
          }
        }

        while (line != null) {
          val Array(firstHand, secondHand) = line.split("-")
          val cardFormat1 = score(firstHand.split(" "))
          val cardFormat2 = score(secondHand.split(" "))
          val score1 = cardsMap(firstHand.split(" ").head)
          val score2 = cardsMap(secondHand.split(" ").head)

          val compared = (cardFormat1, cardFormat2) match {
            case ("*", "*") =>
              score1.compareTo(score2)
            case ("*", _) =>
              1
            case (_, "*") =>
              -1
            case (v1, v2) if v1 == v2 =>
              score1.compareTo(score2)
            case _ =>
              0
          }
          if (compared < 0)
            println(secondHand)
          else if (compared > 0)
            println(firstHand)
          else
            println("ERROR")

          line = br.readLine()
        }
      }
    }


    Main.main(Array.empty[String])

  }

  test("3rd") {
    /*
    [编程题]扑克牌大小
时间限制：C/C++ 10秒，其他语言20秒

空间限制：C/C++ 128M，其他语言256M

扑克牌游戏大家应该都比较熟悉了，一副牌由54张组成，含3~A，2各4张，小王1张，大王1张。牌面从小到大用如下字符和字符串表示（其中，小写joker表示小王，大写JOKER表示大王）:)
3 4 5 6 7 8 9 10 J Q K A 2 joker JOKER
输入两手牌，两手牌之间用“-”连接，每手牌的每张牌以空格分隔，“-”两边没有空格，如：4 4 4 4-joker JOKER
请比较两手牌大小，输出较大的牌，如果不存在比较关系则输出ERROR

基本规则：
（1）输入每手牌可能是个子，对子，顺子（连续5张），三个，炸弹（四个）和对王中的一种，不存在其他情况，由输入保证两手牌都是合法的，顺子已经从小到大排列；
（2）除了炸弹和对王可以和所有牌比较之外，其他类型的牌只能跟相同类型的存在比较关系（如，对子跟对子比较，三个跟三个比较），不考虑拆牌情况（如：将对子拆分成个子）
（3）大小规则跟大家平时了解的常见规则相同，个子，对子，三个比较牌面大小；顺子比较最小牌大小；炸弹大于前面所有的牌，炸弹之间比较牌面大小；对王是最大的牌；
（4）输入的两手牌不会出现相等的情况。

答案提示：
（1）除了炸弹和对王之外，其他必须同类型比较。
（2）输入已经保证合法性，不用检查输入是否是合法的牌。
（3）输入的顺子已经经过从小到大排序，因此不用再排序了.

输入描述:
输入两手牌，两手牌之间用“-”连接，每手牌的每张牌以空格分隔，“-”两边没有空格，如4 4 4 4-joker JOKER。

输出描述:
输出两手牌中较大的那手，不含连接符，扑克牌顺序不变，仍以空格隔开；如果不存在比较关系则输出ERROR。

输入例子1:
4 4 4 4-joker JOKER

输出例子1:
joker JOKER
     */

    object Main {

      import java.io.{BufferedReader, ByteArrayInputStream, InputStreamReader}

      def main(args: Array[String]): Unit = {
        val inputStream = System.in

        val br = new BufferedReader(new InputStreamReader(inputStream))
        var line = br.readLine()

        val cardsMap = Map(
          "3" -> 0,
          "4" -> 1,
          "5" -> 2,
          "6" -> 3,
          "7" -> 4,
          "8" -> 5,
          "9" -> 6,
          "10" -> 7,
          "J" -> 8,
          "Q" -> 9,
          "K" -> 10,
          "A" -> 11,
          "2" -> 12,
          "joker" -> 13,
          "JOKER" -> 14
        )

        def score(cards: Array[String]): String = {
          cards.length match {
            case 1 | 3 | 5 => "normal" + cards.length
            case 2 =>
              if (cards.contains("JOKER") && cards.contains("joker"))
                "*"
              else
                "normal2"
            case 4 =>
              "*"
          }
        }

        while (line != null) {
          val Array(firstHand, secondHand) = line.split("-")
          val cardFormat1 = score(firstHand.split(" "))
          val cardFormat2 = score(secondHand.split(" "))
          val score1 = cardsMap(firstHand.split(" ").head)
          val score2 = cardsMap(secondHand.split(" ").head)

          val compared = (cardFormat1, cardFormat2) match {
            case ("*", "*") =>
              score1.compareTo(score2)
            case ("*", _) =>
              1
            case (_, "*") =>
              -1
            case (v1, v2) if v1 == v2 =>
              score1.compareTo(score2)
            case _ =>
              0
          }
          if (compared < 0)
            println(secondHand)
          else if (compared > 0)
            println(firstHand)
          else
            println("ERROR")

          line = br.readLine()
        }
      }
    }


    Main.main(Array.empty[String])

  }

}
